var Utils = require('geoUtils');

var NodeUtils = {
    /**
     * TODO: 考虑移动到别的分类下。
     * 将sizeFrom扩展或收缩以逼近sizeTo，并维持原宽搞比例。
     * @param {cc.Size} sizeFrom 
     * @param {cc.Size} sizeTo 
     * @param {boolean} useWidth 使用宽作为参考
     * @param {boolean} useHeight 使用高作为参考
     * @returns {cc.Size} 返回逼近后的结果，若useWidth和useHeight同时为true或false，
     * 则可以保证sizeFrom在sizeTo范围内进行最优逼近。
     */
    fitSize(sizeFrom, sizeTo, useWidth, useHeight) {
        // //
        // var ratio = Infinity;
        // var ratioW = sizeTo.width / sizeFrom.width;
        // var ratioH = sizeTo.height / sizeFrom.height;
        // if (useWidth !== true && useHeight !== true) {
        //     useWidth = useHeight = true;
        // }
        // // 选定比例
        // if (useWidth === true)
        //     ratio = ratioW;
        // if (useHeight === true && ratioH < ratio)
        //     ratio = ratioH;
        //
        var ratio = Utils.misc.getScaleRatio(sizeFrom, sizeTo, useWidth, useHeight);
        //
        var width = sizeFrom.width * ratio;
        var height = sizeFrom.height * ratio;
        //
        return new cc.Size(width, height);
    },
    /**
     * 将nodeFrom的尺寸逼近nodeTo的尺寸，保持宽高比例
     * @param {cc.Node} nodeFrom 
     * @param {cc.Node} nodeTo 
     * @param {boolean} useWorldReference 是否使用世界参考
     * @param {boolean} useWidth 
     * @param {boolean} useHeight 
     */
    fitNodeSize(nodeFrom, nodeTo, useWorldReference, useWidth, useHeight) {
        var sizeFrom =
            (useWorldReference === true) ?
                NodeUtils.getWorldSize(nodeFrom) : nodeFrom.getContentSize();
        var sizeTo =
            (useWorldReference === true) ?
                NodeUtils.getWorldSize(nodeTo) : nodeTo.getContentSize();
        //
        var size = NodeUtils.fitSize(sizeFrom, sizeTo, useWidth, useHeight);
        //
        (useWorldReference === true) ?
            NodeUtils.setWorldSize(nodeFrom, size) : nodeFrom.setContentSize(size);
    },
    /**
     * 将指定node显示在其父节点的最底层，视觉上会被其它节点挡住。
     * @param {cc.Node} node 控制对象
     */
    showToBottom(node){
        node.setSiblingIndex(0);
    },
    /**
     * 将指定node显示在其父节点的最定层，视觉上会挡住其它节点。
     * 适用于将某节点显示到其它节点前面的防止被遮挡的情形。
     * @param {cc.Node} node 
     */
    showToTop(node){
        node.setSiblingIndex(node.parent.childrenCount - 1);
    },
    /**
     * 将parentNode设置为childNode的父节点
     * @param {cc.Node} parentNode 
     * @param {cc.Node} childNode 
     * @param {bool} keepWorldTransform true，则维持childNode之前的世界变换
     */
    setParent(parentNode, childNode, keepWorldTransform) {
        if (parentNode === childNode.parent)
            return;

        if (keepWorldTransform === true) {
            // keep world transform not changed
            let worldPos = NodeUtils.getWorldPosition(childNode);
            let worldRotation = NodeUtils.getWorldRotation(childNode);
            let lossyScale = NodeUtils.getWorldScale(childNode);

            childNode.parent = parentNode;

            // restore world transform
            NodeUtils.setWorldPosition(childNode, worldPos);
            NodeUtils.setWorldRotation(childNode, worldRotation);
            if (parentNode) {
                let parentWorldScale = NodeUtils.getWorldScale(parentNode);
                lossyScale.x /= parentWorldScale.x;
                lossyScale.y /= parentWorldScale.y;
                childNode.scale = lossyScale;
            } else {
                childNode.scale = lossyScale;
            }
        } else {
            childNode.parent = parentNode;
        }
    },
    /**
     * 将childNodes数组中的节点全部归为parentNode的子节点
     * @param {cc.Node} parentNode 父节点
     * @param {[cc.Node]} childNodes 子节点数组
     * @param {boolean} keepWorldTransform 是否保持世界变换
     * @example
     * 若要将nodeB下所有孩子转移到nodeA下，可以如下调用方法
     * setParentForNodes(nodeA, nodeB.children, true/false)
     */
    setParentForNodes(parentNode, childNodes, keepWorldTransform) {
        childNodes.forEach(childNode => {
            NodeUtils.setParent(parentNode, childNode, keepWorldTransform);
        });
    },
    /**
     * 获得node的世界坐标
     * @param {cc.Node} node 
     * @returns {cc.Vec2}
     */
    getWorldPosition(node) {
        return node.parent.convertToWorldSpaceAR(node.position);
    },
    /**
     * 将node设置到世界坐标worldPos处
     * @param {cc.Node} node
     * @param {cc.Vec2} worldPos
     */
    setWorldPosition(node, worldPos) {
        var parent = node.parent;
        if (parent) {
            node.position = parent.convertToNodeSpaceAR(worldPos);
        } else {
            node.position = worldPos;
        }
        return node.position;
    },
    /**
     * 获得node在世界坐标系下的旋转
     * @param {cc.Node} node 
     * @return {float} node的世界旋转
     */
    getWorldRotation(node) {
        return NodeUtils.getNodeToWorldRotation(node.parent, node.rotation);
    },
    /**
     * 将node设置为世界坐标系下旋转worldRotation的姿态
     * @param {cc.Node} node 
     * @param {cc.Vec2} worldRotation 
     * @returns {float} node的本地旋转
     */
    setWorldRotation(node, worldRotation) {
        node.rotation = NodeUtils.getWorldToNodeRotation(node.parent, worldRotation);
        //
        return node.rotation;
    },
    /**
     * 获得node在世界坐标下的缩放
     * @param {cc.Node} node 
     * @returns {cc.Vec2} node的世界缩放
     */
    getWorldScale(node) {
        return NodeUtils.getNodeToWorldScale(node.parent, node.scaleX, node.scaleY);
    },
    /**
     * 将node设置为在世界坐标下缩放worldScale的形态
     * @param {cc.Node} node 
     * @param {cc.Vec2} worldScale 
     * @returns {cc.Vec2} node的本地缩放
     */
    setWorldScale(node, worldScale) {
        var s = NodeUtils.getWorldToNodeScale(node.parent, worldScale.x, worldScale.y);
        node.scale = s;
        //
        return s;
    },
    /**
     * 获取node在世界坐标下的尺寸
     * @param {cc.Node} node 
     * @returns {cc.Size} node在世界坐标下的尺寸
     */
    getWorldSize(node) {
        var scale = NodeUtils.getWorldScale(node);
        var size = node.getContentSize();
        var width = size.width * Math.abs(scale.x);
        var height = size.height * Math.abs(scale.y);
        return new cc.Size(width, height);
    },
    /**
     * 设置node在世界坐标下的尺寸
     * @param {cc.Node} node 
     * @param {cc.Size} sizeInWorld 
     */
    setWorldSize(node, sizeInWorld) {
        var scale = NodeUtils.getWorldScale(node);
        var width = sizeInWorld.width / Math.abs(scale.x);
        var height = sizeInWorld.height / Math.abs(scale.y);
        node.setContentSize(width, height);
    },
    /**
     * 获得node本地坐标系下的点在世界坐标系下的位置
     * @param {cc.Node} node 
     * @param {cc.Vec2} positionInNode 
     */
    getNodeToWorldPosition(node, positionInNode) {
        if (node === null)
            return positionInNode;
        return node.convertToWorldSpaceAR(positionInNode);
    },
    /**
     * 获得世界坐标系下的点在node本地坐标下的位置
     * @param {cc.Node} node 
     * @param {cc.Vec2} positionInWorld 
     */
    getWorldToNodePosition(node, positionInWorld) {
        if (node === null)
            return positionInWorld;
        return node.convertToNodeSpaceAR(positionInWorld);
    },
    /**
     * 获得node本地坐标下的缩放在世界坐标下的缩放量
     * @param {cc.Node} node 
     * @param {float} scaleXInNode 
     * @param {float} scaleYInNode 
     */
    getNodeToWorldScale(node, scaleXInNode, scaleYInNode) {
        // if (node === null)
        //     return new cc.Vec2(scaleXInNode, scaleYInNode);
        var scaleX = scaleXInNode;//node.scaleX;
        var scaleY = scaleYInNode;//node.scaleY;
        var parent = node;//.parent;
        while (parent) {// && parent.parent) {
            scaleX *= parent.scaleX;
            scaleY *= parent.scaleY;
            parent = parent.parent;
        }
        // return new cc.Vec2(scaleX, scaleY);
        return new cc.Vec2(scaleX, scaleY);
    },
    /**
     * 获得世界缩放在node本地坐标下的缩放量
     * @param {cc.Node} node 
     * @param {float} scaleXInWorld 
     * @param {float} scaleYInWorld 
     */
    getWorldToNodeScale(node, scaleXInWorld, scaleYInWorld) {
        // if (node === null)
        //     return new cc.Vec2(scaleXInWorld, scaleYInWorld);
        // var scale = NodeUtils.getNodeToWorldScale(node.parent, node.scaleX, node.scaleY);
        var scale = NodeUtils.getWorldScale(node);
        return new cc.Vec2(scaleXInWorld / scale.x, scaleYInWorld / scale.y);
    },
    /**
     * 获得node本地坐标下的旋转在世界坐标下的旋转量
     * @param {cc.Node} node 
     * @param {float} rotationInNode 
     */
    getNodeToWorldRotation(node, rotationInNode) {
        return NodeUtils._getRotation(node, rotationInNode, true);
        // if (node === null)
        //     return rotationInNode;
        // var rot = rotationInNode;//node.rotationX;
        // var parent = node;//.parent;
        // var sign = 1;
        // while (parent) {//} && parent.parent) {
        //     sign = Math.sign(parent.scaleX * parent.scaleY);
        //     rot = rot * sign + parent.rotation;
        //     parent = parent.parent;
        // }
        // return rot;
    },
    /**
     * 获得世界坐标下的旋转在node本地坐标下的旋转量
     * @param {cc.Node} node 
     * @param {float} rotationInWorld 
     */
    getWorldToNodeRotation(node, rotationInWorld) {
        return NodeUtils._getRotation(node, rotationInWorld, false);
        // if (node === null)
        //     return rotationInWorld;
        // var rot = rotationInWorld;
        // // rotation -= node.rotation;
        // var parent = node;//.parent;
        // var sign = 1;
        // while (parent) {//} && parent.parent) {
        //     sign = Math.sign(parent.scaleX * parent.scaleY);
        //     rot = rot * sign - parent.rotation;
        //     parent = parent.parent;
        // }
        // return rot;
    },
    /**
     * 内部方法，不建议直接使用
     */
    _getRotation(node, rotatioin, isNodeToWorld) {
        // if (node === null)
        //     return rotationInNode;
        var rot = rotatioin;//node.rotationX;
        var parent = node;//.parent;
        var sign = 1;
        while (parent) {//} && parent.parent) {
            sign = Math.sign(parent.scaleX * parent.scaleY);
            rot *= sign;
            if (isNodeToWorld == true)
                rot += parent.rotation;
            else
                rot -= parent.rotation;
            //
            parent = parent.parent;
        }
        return rot;
    },
    /**
     * 获得nodeA下的坐标在nodeB下的坐标值
     * @param {cc.Node} nodeA 
     * @param {cc.Vec2} positionInNodeA 
     * @param {cc.Node} nodeB 
     * @return {cc.Vec2} positionInNodeB
     */
    getNodeToNodePosition(nodeA, positionInNodeA, nodeB) {
        var p = NodeUtils.getNodeToWorldPosition(nodeA, positionInNodeA);
        return NodeUtils.getWorldToNodePosition(nodeB, p);
    },
    /**
     * 获得nodeA下的旋转在nodeB下的旋转值
     * @param {cc.Node} nodeA 
     * @param {float} rotationInNodeA 
     * @param {cc.Node} nodeB 
     * @return {float} rotationInNodeB
     */
    getNodeToNodeRotation(nodeA, rotationInNodeA, nodeB) {
        var r = NodeUtils.getNodeToWorldRotation(nodeA, rotationInNodeA);
        return NodeUtils.getWorldToNodeRotation(nodeB, r);
    },
    /**
     * 获得nodeA下的缩放在nodeB下的缩放值
     * @param {cc.Node} nodeA 
     * @param {cc.Vec2} scaleInNodeA 
     * @param {cc.Node} nodeB 
     * @return {cc.Vec2} scaleInNodeB
     */
    getNodeToNodeScale(nodeA, scaleInNodeA, nodeB) {
        var s = NodeUtils.getNodeToWorldScale(nodeA, scaleInNodeA.x, scaleInNodeA.y);
        return NodeUtils.getWorldToNodeScale(nodeB, s.x, s.y);
    },
    /**
     * 获得节点两边节点
     * @param {cc.Node} node 
     * @returns {[cc.Node]} 若两边节点都存在，则[0]为前节点，[1]为后节点
     */
    getSiblings(node) {
        var ret = [];
        var before = NodeUtils.getSiblingBefore(node);
        var after = NodeUtils.getSiblingAfter(node);
        before && ret.push(before);
        after && ret.push(after);
        return ret;
    },
    /**
     * 获得节点前边的节点
     * @param {cc.Node} node 
     */
    getSiblingBefore(node) {
        if (node.parent == null)
            return null;
        var i = node.getSiblingIndex();
        if (i === 0)
            return null;
        return node.parent.children[i - 1];
    },
    /**
     * 获得节点后面的节点
     * @param {cc.Node} node 
     */
    getSiblingAfter(node) {
        if (node.parent == null)
            return null;
        var i = node.getSiblingIndex();
        if (i === node.parent.children.length - 1)
            return null;
        return node.parent.children[i + 1];
    },
    /**
     * 对node的孩子节点进行乱序
     * @param {cc.Node} node 
     */
    randomChildren(node) {
        var children = node.children;
        children = Utils.array.random(children, children.length);
        children.forEach(c => {
            c.setSiblingIndex(0);
        });
    },
    /**
     * 让nodeLook看向nodeAt
     * @param {cc.Node} nodeLook 观察节点
     * @param {cc.Node} nodeAt 目标节点
     * @param {float} delta 偏移角（用于矫正图片朝向，通常为-90，90，180）
     * @param {cc.Vec2} face 观察节点的参考朝向，默认为0旋转时(1,0)向量
     */
    lookAt(nodeLook, nodeAt, delta, face) {
        delta = delta || 0;
        face = face || new cc.Vec2(1, 0);
        // 获得nodeLook的世界坐标
        var pLook = NodeUtils.getWorldPosition(nodeLook);
        var pAt = NodeUtils.getWorldPosition(nodeAt);
        // 计算观察向量
        var vLook = pAt.sub(pLook);
        // 计算角度
        var vOne = face;
        // change for ccc 2.0.1
        // var a = cc.radiansToDegrees(vOne.signAngle(vLook));
        var a = cc.misc.radiansToDegrees(vOne.signAngle(vLook));
        NodeUtils.setWorldRotation(nodeLook, -a + delta);
    },
    /**
     * getNodesInHierarchyByNameMark的简写形式。
     * 推荐使用此简写形式。
     */
    getNodesInHierarchy(node, markName, markValue) {
        return NodeUtils.getNodesInHierarchyByNameMark(node, markName, markValue);
    },
    /**
     * 遍历node所有层级，收集指定类型的com组件
     * @param {cc.Node} node 
     * @param {cc.Component} componentTypeOrName 
     * @param {*} markName 标记名
     * @param {*} markValue 标记值
     */
    getComponentsInHierarchy(node, componentTypeOrName, markName, markValue) {
        var coms = [];
        NodeUtils.walk(node, n => {
            let com = n.getComponent(componentTypeOrName);
            com && coms.push(com);
        }, null, markName, markValue);
        //
        return coms;
    },

    /**
     * 在给定节点以及父节点中寻找指定组件
     * @param {cc.Node} node
     * @param {cc.Component | string} componentTypeOrName 
     * @returns {cc.Component} null或者找到的组件。
     */
    getComponentInParents(node, componentTypeOrName) {
        var parent = node;
        var com = null;
        // 不检查世界节点(parent为null的是世界节点)
        while (parent && parent.parent) {
            com = parent.getComponent(componentTypeOrName);
            if (com) return com;
            // 向更上一层父级搜寻
            parent = parent.parent;
        }
        //
        return null;
    },
    /**
     * 深度优先遍历node所有层级，收集节点命名带有特定标记的节点，如
     * 某节点自身及其子树节点中带有这样的标记“?mark=a”，那么
     * getNodesInHierarchyByNameMark(node, 'mark', 'a')就可收集
     * 所有带有这个标记的节点。
     * 本方法特别适用于收集一些特殊标记的节点，然后做处理。
     * @param {*} node 层级根节点
     * @param {*} markName 标记名，若不指定，则返回所有子孙节点。
     * @param {*} markValue 标记值， 若不指定，则仅考虑markName是否存在。
     * @returns 符标记条件的所有节点
     */
    getNodesInHierarchyByNameMark(node, markName, markValue) {
        var rets = [];
        //
        NodeUtils.walk(node, n => {
            rets.push(n);
        }, null, markName, markValue);
        //
        return rets;
    },
    /**
     * 设置节点的分组
     * @param {cc.Node} node 被设置的节点
     * @param {string|int} groupNameOrIndex 分组名或分组索引 
     * @param {bool} useHierarchy 是否同时设置所有子层级
     */
    setNodeGroup(node, groupNameOrIndex, useHierarchy) {
        // 根据groupNameOrIndex是不是数字决定使用group或是groupIndex方法
        var funcToUse = isNaN(Number.parseInt(groupNameOrIndex)) ? 'group' : 'groupIndex';
        //
        if (useHierarchy === true) {
            NodeUtils.walk(node, n => {
                n[funcToUse] = groupNameOrIndex;
            })
        } else {
            node[funcToUse] = groupNameOrIndex;
        }
    },
    /**
     * 同步一个节点和另一个节点的世界空间变换属性
     * @param {cc.Node} fromNode 
     * @param {cc.Node} toNode 
     * @param {bool} syncPosition 
     * @param {bool} syncRotation 
     * @param {bool} syncScale 
     */
    syncTransform(fromNode, toNode, syncPosition, syncRotation, syncScale) {
        //
        if (syncPosition == true) {
            let p = NodeUtils.getWorldPosition(fromNode);
            NodeUtils.setWorldPosition(toNode, p);
        }
        //
        if (syncRotation == true) {
            let r = NodeUtils.getWorldRotation(fromNode);
            NodeUtils.setWorldRotation(toNode, r);
        }
        //
        if (syncScale == true) {
            let r = NodeUtils.getWorldScale(fromNode);
            NodeUtils.setWorldScale(toNode, r);
        }
    },
    /**
     * 重写官方walk，避开官方bug
     * @param {*} node 
     * @param {*} prefunc 
     * @param {*} postfunc 
     * @param {String} markName 用于过滤节点的标记名
     * @param {String|Number} markValue 用于过滤节点的标记值  
     */
    walk(node, prefunc, postfunc, markName, markValue) {

        var stopRecord = false;
        node.walk(n => {
            // BUG 弥补官方walk回溯时超出当前节点范围
            if (stopRecord === true)
                return;
            prefunc && NodeUtils.matchMark(n, markName, markValue) && prefunc(n);
        }, n => {
            if (stopRecord === true)
                return;
            postfunc && NodeUtils.matchMark(n, markName, markValue) && postfunc(n);
            // 收尾，弥补官方walk的bug
            (n == node) && (stopRecord = true);
        });
    },
    /**
     * 匹配节点上的标记。
     * @param {*} node 待检测节点。
     * @param {*} markName 标记名，若为null或‘’，则永远返回true。
     * @param {*} markValue 标记值，若为null或‘’，则仅考虑标记名。
     * @returns true，node携带的标记与指定的匹配；false，不匹配。
     */
    matchMark(node, markName, markValue) {
        if (markName == null || markName == '') {
            // 若为null或‘’，则永远返回true。
            return true;
        } else {
            // 获取标记值
            var v = Utils.text.getQueryString(node.name, markName);
            // 标记名不存在，匹配失败
            if (v === undefined)
                return false;
            // 标记名存在，考察标记值
            if (markValue == null || markValue == '' || markValue == v)
                return true;
            //
            return false;
        }
    },
    /**
     * 获取节点上的组件，如果没有就添加。
     * @param {*} node 
     * @param {*} componentTypeOrName 
     * @returns 获取的或添加的组件。
     */
    getComponentWhatever(node, componentTypeOrName) {
        var c = node.getComponent(componentTypeOrName);
        if (c == null)
            c = node.addComponent(componentTypeOrName);
        //
        return c;
    },
    /**
     * 设置一个节点（或包含其子孙节点）某类组件的指定属性值。
     * @param {*} node 被设置的节点。
     * @param {*} componentTypeOrName 目标组件类型或名称。
     * @param {*} attrs 要设置的组件属性名及值。
     * @param {*} includeHierarchy 是否考虑子孙。
     * @param {*} markNameInHierarchy 列为考虑对象的子孙的标记名。
     * @param {*} markValueInHierarchy 列为考虑对象的子孙的标记值。
     * @param {*} addIfNotExists 若组件不存在是否自动添加。
     * @param {Function} validateFunc 返回true，则对遍历到的节点进行属性复制。
     * validateFunc(node){
     *  ... // 分析节点
     *  return true|false;
     * }
     * 默认总是会复制。
     */
    setComponentAttrs(node, componentTypeOrName, attrs,
        includeHierarchy, markNameInHierarchy, markValueInHierarchy,
        addIfNotExists, validateFunc) {
        if (includeHierarchy !== true) {
            // 只考虑当前节点node
            if (validateFunc == null || validateFunc(node) === true)
                NodeUtils._setComponentAttrs(node, componentTypeOrName, attrs, addIfNotExists);
        } else {
            // 考虑当前节点及所有子孙
            NodeUtils.walk(node, n => {
                if (validateFunc == null || validateFunc(n) === true)
                    NodeUtils._setComponentAttrs(n, componentTypeOrName, attrs, addIfNotExists);
            }, null, markNameInHierarchy, markValueInHierarchy);
        }
    },
    /**
     * 组件效仿工具。
     * 从fromNode上获取componentName指定的组件，并将attrs中指定的属性，复制到toNode或其子孙节点。
     * @param {*} fromNode 被效仿的节点。
     * @param {*} componentTypeOrName 被效仿的组件名，用于从fromNode上获取组件。
     * @param {*} attrs 被效仿的属性名列表，如['name', 'position', 'target']。
     * @param {*} toNode 效仿节点。
     * @param {*} includeHierarchy 是否考虑toNode的子孙。
     * @param {*} includeToNode 是否考虑toNode本身，有时toNode仅作为一个根节点。
     * @param {*} markName 用于筛选节点的标记名，使用空值则考虑所有遍历到的节点。
     * @param {*} markValue 用于筛选节点的标记值，使用空值则仅考虑markName的存在。
     * @param {*} addIfNotExist 为true，则自动为不携带指定组件的节点附加组件。
     * @remark 包装setComponentAttrs，使用更便捷的方法，但没有setComponentAttrs灵活。
     */
    imitateComponent(fromNode, componentTypeOrName, attrs, toNode,
        includeHierarchy, includeToNode, markNameInHierarchy, markValueInHierarchy, addIfNotExist) {
        var comFrom = fromNode.getComponent(componentTypeOrName);
        var comAttrs = Utils.obj.getAttrs(comFrom, attrs);
        //
        NodeUtils.setComponentAttrs(toNode, componentTypeOrName, comAttrs,
            includeHierarchy, markNameInHierarchy, markValueInHierarchy, addIfNotExist,
            n => (includeToNode || (n != toNode)));
    },
    /**
     * 将attrs携带的属性信息复制给node的指定组件。
     * @param {*} node 
     * @param {*} componentTypeOrName 
     * @param {*} attrs 
     * @param {*} addIfNotExists 
     */
    _setComponentAttrs(node, componentTypeOrName, attrs, addIfNotExists) {
        var compTo = node.getComponent(componentTypeOrName);
        if (compTo == null && addIfNotExists === true) {
            compTo = node.addComponent(componentTypeOrName);
        }
        // 复制指定属性
        compTo && Utils.obj.copyAttrs(attrs, compTo);
        //
        return compTo;
    },
    /**
     * 获取节点的所有标签信息
     */
    getMarks(node) {
        Utils.text.parseQueryString(node.name);
    },
    /**
     * 通过节点的名字获得其特征
     * @param {*} node 
     * @param {*} featureMark 包含特征的标签名
     * @returns 返回一个特征值数组
     */
    getFeatures(node, featureMark) {
        var featureString = Utils.text.getQueryString(node.name, featureMark);
        if (featureString != null)
            return featureString.split(' ');
        else
            return [];
    },
};

module.exports = NodeUtils;